package main.java;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.TreeMap;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import main.java.utils.Utils;

/**
 * Through this package, all is done with default jre methods
 * 
 * @author patrick
 * 
 */
public class Castor {
  
  /**
   * The logger for this class
   */
  private final Logger slf4j = LoggerFactory.getLogger(this.getClass());

  /**
   * Files list to be parsed
   */
  private Collection<File> _files;

  /**
   * expression for splitting a path and a value from a line
   */
  public static final Pattern splitterPathValue = Pattern.compile(" : ");

  /**
   * expression for parsing a tree path
   */
  public static final Pattern splitterPath = Pattern.compile("\\/|\\.");

  /**
   * Default constructor
   * 
   * @param files is the array of files to parse.
   */
  public Castor(final Collection<File> files) {
    _files = files;
  }
  
  /**
   * Default constructor
   * 
   * @param files is the array of files to parse.
   */
  public Castor(final String[] files) {
    List<File> l = new ArrayList<File>(files.length);
    
    for (String file : files) {
      try {
      File f = new File(file);
      if (f.exists() && f.isFile())
        l.add(f);
      }
      catch (NullPointerException npe) {
        slf4j.warn("Trying to construct a File, but an error occurs. Ignoring it and going next");
      }
    }
    _files = l;
  }  

  public final TreeMap<String, Integer> readAndComputeFiles(final List<File> files) throws IllegalArgumentException, IOException {
    _files = files;
    return readAndComputeFiles();
  }
  
  /**
   * Read the files one by one, parse it, and compute the values.
   * Complexity : O(m.n.o. log(o))
   * 
   * @return a TreeMap with the summation of values for each nodes
   * @throws IllegalArgumentException if no files to compute
   * @throws IOException if an error occurs on reading a file
   */
  public final TreeMap<String, Integer> readAndComputeFiles() throws IllegalArgumentException, IOException {
    if (_files == null || _files.size() < 1)
      throw new IllegalArgumentException("No files to process");

    // pas thread-safe, utiliser une SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...)) si besoin;
    TreeMap<String, Integer> res = new TreeMap<String, Integer>();

    // pas optimisé si beaucoup de fichier à traiter.
    for (final File file : _files) {

      // Pas optimisé pour de gros fichiers car tout monte en mémoire.
      final List<String> linesInFile = Files.readAllLines(file.toPath(), Charset.defaultCharset());
      for (final String line : linesInFile) {
        final String[] splitted = splitterPathValue.split(line);

        if (splitted.length == 2) { // si != 2 c'est que la ligne n'est pas valide
          final String[] wordList = splitterPath.split(splitted[0]);
          final int value = Integer.parseInt(splitted[1], 10);
          
          // Traitement du path complet pour une ligne donnée
          for (int i = 0; i < wordList.length; ++i) {
            String key = wordList[i];
            if (i != 0)
              // slice and join
              key = Utils.join(Arrays.asList(Arrays.copyOf(wordList, i+1)), '/');
            
            if (res.containsKey(key)) {
              res.put(key, res.get(key) + value);
            } else {
              res.put(key, value);
            }
          }
        }
      }
    }

    return res;
  }
  
  /**
   * @param args files to compute together
   */
  public static void main(String[] files) {
    final Logger slf4j = LoggerFactory.getLogger("Castor::main");
    Castor m = new Castor(files);

    try {
      m.readAndComputeFiles();
    } catch (IllegalArgumentException e) {
      slf4j.error("no files to compute");
      e.printStackTrace();
    } catch (IOException e) {
      slf4j.error("an error occurs when trying to open or read files");
      e.printStackTrace();
    }
  }
}
